using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using VisualEsse.DataEngine.Types;
using UVS.UI;
using UVS.Base;
using OpenGL;

namespace Plotter
{
    public partial class PlotterControl : UserControl
    {
        private Dictionary<object, GraphLayer> graphs = new Dictionary<object, GraphLayer>();
        private INonUniformGrid1d grid;
        private LayeredView view = new LayeredView();
        private BackgroundLayer background = new BackgroundLayer();
        private Viewport2dLayer viewport = new Viewport2dLayer();
        private Axes2dLayer axes = new Axes2dLayer();

        public PlotterControl()
        {
            InitializeComponent();
            background.Depth = 3;
            background.Color = Color.White;
            view.Layers.Add(background);
            viewport.Depth = 2;
            axes.Depth = 1;
            singleViewControl.View = view;
        }

        public INonUniformGrid1d Grid
        {
            get
            {
                return grid;
            }
            set
            {
                if (value != null)
                    SetGrid(value);
                else
                    RemoveGrid();
            }
        }

        public void AddGraph(double[] data, Color color, object key)
        {
            if (graphs.ContainsKey(key))
                throw new ArgumentException("Graph layer with specified key already exists!");
            if(grid == null)
                throw new ArgumentException("Grid is not specified. Specify grid first");
            GraphLayer gl = new GraphLayer();
            gl.Depth = 0 - graphs.Count;
            gl.Data = data;
            gl.Grid = grid;
            gl.Color = color;
            view.Layers.Add(gl);
            graphs.Add(key, gl);
            UpdateViewPort();
        }

        public void RemoveGraph(object key)
        {
            // TODO: implement
        }

        private void SetGrid(INonUniformGrid1d grid)
        {
            if (graphs.Count > 0)
                if (this.grid.Length != grid.Length)
                    throw new ArgumentException("Cannot change number of grid points while graphs are displated");
            this.grid = grid;
            UpdateViewPort();
        }

        private void RemoveGrid()
        {
            if (graphs.Count > 0)
                throw new ArgumentException("Cannot remove grid while graphs are active");
            this.grid = null;
            RemoveViewPort();
        }

        private void UpdateViewPort()
        {
            double min = Double.MaxValue, max = Double.MinValue;
            if (graphs.Count > 0)
            {
                foreach (GraphLayer gl in graphs.Values)
                {
                    if (min > gl.Min)
                        min = gl.Min;
                    if (max < gl.Max)
                        max = gl.Max;
                }
            }
            else
            {
                min = 0;
                max = 1;
            }
            RectangleD rect = new UVS.Base.RectangleD(grid.Knots[0], min, grid.Knots[grid.Length - 1], max);
            axes.Axes = rect;
            viewport.Viewport2d.SetDomain(rect);
            if (rect.Width > 5 * rect.Height)
                viewport.Viewport2d.Aspect = rect.Height / rect.Width;
            else if (rect.Width * 5 < rect.Height)
                viewport.Viewport2d.Aspect = rect.Height / rect.Width;
            else
                viewport.Viewport2d.Aspect = 1;
            
            if (viewport.View == null)
            {
                view.Layers.Add(viewport);
                view.Layers.Add(axes);
                view.ResizeAllLayers();
            }
        }

        private void RemoveViewPort()
        {
            if (viewport.View != null)
            {
                view.Layers.Remove(axes);
                view.Layers.Remove(viewport);
            }
        }
    }

    public class GraphLayer : Layer2d
    {
        private double[] data;
        private double min, max;
        private INonUniformGrid1d grid;
        private Color color;
        private const uint NoList = UInt32.MaxValue;
        private uint list = NoList;

        public double[] Data
        {
            get
            {
                return data;
            }
            set
            {
                data = value;
                min = max = data[0];
                for (int i = 1; i < data.Length; i++)
                    if (min > data[i])
                        min = data[i];
                    else if (max < data[i])
                        max = data[i];
            }
        }

        public double Min { get { return min; } }

        public double Max { get { return max; } }

        public INonUniformGrid1d Grid
        {
            get
            {
                return grid;
            }
            set
            {
                grid = value;
            }
        }

        public Color Color
        {
            get
            {
                return color;
            }
            set
            {
                color = value;
            }
        }

        public override void OnContextAttached()
        {
            base.OnContextAttached();
            if (data != null && grid != null)
                CreateList();
        }

        public override void OnContextDetached()
        {
            base.OnContextDetached();
            if (list != NoList)
                DeleteList();
        }

        public override void OnRender(Viewport2d viewport)
        {
            base.OnRender(viewport);
            if (data == null || grid == null)
                return;
            if (list == NoList)
                CreateList();
            GL.glColor(color);
            GL.glMatrixMode(GL.GL_MODELVIEW);
            GL.glPushMatrix();
            GL.glLoadIdentity();
            GL.glTranslated(viewport.Output.Left, viewport.Output.Top, 0);
            GL.glScaled(viewport.Output.Width / (double)viewport.Visible.Width,
                viewport.Output.Height / (double)viewport.Visible.Height,1);
            GL.glTranslated(-viewport.Visible.Xmin, -viewport.Visible.Ymin, 0);
            GL.glCallList(list);
            GL.glPopMatrix();
        }

        private void CreateList()
        {
            list = GL.glGenLists(1);
            GL.glNewList(list, GL.GL_COMPILE);
            GL.glColor(color);
            GL.glBegin(GL.GL_LINE_STRIP);
            for (int i = 0; i < data.Length; i++)
                GL.glVertex2d(grid.Knots[i], data[i]);
            GL.glEnd();
            GL.glEndList();
        }

        private void DeleteList()
        {
            GL.glDeleteLists(list, 1);
            list = NoList;
        }
    }
}
